home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_46 / dma_code.asm < prev    next >
Assembly Source File  |  1995-01-01  |  16KB  |  411 lines

  1. ; DMA_CODE.ASM
  2.  
  3.                 IDEAL
  4.                 Model large
  5.  
  6. ;+---------------------------------------------------------------------------+
  7. ;|  IBM-PC(tm) compatible programmer's DMA library                           |
  8. ;|  Version 2.0 by Christopher M. Box                                        |
  9. ;+---------------------------------------------------------------------------+
  10. ;|  Version 1.1 copyright (C) 1992, Heath I Hunnicutt                        |
  11. ;+---------------------------------------------------------------------------+
  12. ;|  Thanks to: Gary Nemirovsky, Heath I Hunnicutt                            |
  13. ;+---------------------------------------------------------------------------+
  14. ;|  This document is for free public distribution.  It is unlawful to        |
  15. ;|  sell this document, or any work based substantially upon it.             |
  16. ;+---------------------------------------------------------------------------+
  17. ;| PUBLIC DMA FUNCTIONS                                                      |
  18. ;| int dma_setup(int Channel,char far *Buffer,unsigned Length,int Dir)       |
  19. ;| int prevent_dma(int Channel)                                              |
  20. ;| int allow_dma(int Channel)                                                |
  21. ;| unsigned dma_count(int Channel)                                           |
  22. ;| unsigned dma_addr(int Channel)                                            |
  23. ;+---------------------------------------------------------------------------+
  24. ;| PUBLIC DATA                                                               |
  25. ;| int dma_errno                                                             |
  26. ;| char far *dma_errlist[]                                                   |
  27. ;+---------------------------------------------------------------------------+
  28. ;| HISTORY:                                                                  |
  29. ;|   Ver 1.0 - Initial Release                                               |
  30. ;|   Ver 1.1 - Error checking and reporting added to all functions           |
  31. ;|             dma_setup(..) should never crash your system now.             |
  32. ;|   Ver 1.2 - Changed mode to auto-initialise (Mod. by CMB)                 |
  33. ;|   Ver 2.0 - Major changes to deal with 2nd DMA controller (by CMB)        |
  34. ;+---------------------------------------------------------------------------+
  35.  
  36. Status          EQU     08h     ;DMAC status port (read)     \  same port
  37. Command         EQU     08h     ;DMAC command port (write)   /  (read/write)
  38. Request         EQU     09h     ;DMAC channel request (write-only)
  39. DMA_Mask        EQU     0Ah     ;DMAC DMA_Mask (write-only)
  40. Mode            EQU     0Bh     ;DMAC mode (write)
  41. byte_ptr        EQU     0Ch     ;byte pointer flip-flop
  42.  
  43. addr            EQU  000h  ; per-channel base address
  44. count           EQU  001h  ; per-channel byte count
  45.  
  46. IFDEF NON_AUTOINIT
  47. read_cmd        EQU     048h    ; read mode
  48. write_cmd       EQU     044h    ; write mode
  49. ELSE
  50. read_cmd        EQU     058h    ; autoinitialising read
  51. write_cmd       EQU     054h    ; auto write
  52. ENDIF
  53.  
  54. set_cmd         EQU     000h    ; DMA_Mask set (enable dma)
  55. reset_cmd       EQU     004h    ; DMA_Mask reset (disable)
  56.  
  57.                 DATASEG
  58. ; dma controller page register table
  59. ; this table maps from channel number to the i/o port number of the
  60. ; page register for that channel
  61.  
  62. page_table  DW  00087h  ; channel 0
  63.             DW  00083h  ; channel 1
  64.             DW  00081h  ; channel 2
  65.             DW  00082h  ; channel 3
  66.             DW  0ffffh  ; ch 4 (not used)
  67.             DW  0008Bh  ; ch 5
  68.             DW  00089h  ; ch 6
  69.             DW  0008Ah  ; ch 7
  70.  
  71. dmac2        DB 0    ; Flag set to non-zero when using the 2nd DMA controller
  72.  
  73. ; "Extra" messages are for future compatability with the Virtual DMA
  74. ; specification.
  75. DMA_E0          DB      0
  76. DMA_E1          DB      "Region not in contiguous memory.",0
  77. DMA_E2          DB      "Region crossed a physical alignment boundary.",0
  78. DMA_E3          DB      "Unable to lock pages.",0
  79. DMA_E4          DB      "No buffer available.",0
  80. DMA_E5          DB      "Region too large for buffer.",0
  81. DMA_E6          DB      "Buffer currently in use.",0
  82. DMA_E7          DB      "Invalid memory region.",0
  83. DMA_E8          DB      "Region was not locked.",0
  84. DMA_E9          DB      "Number of physical pages greater than table length.",0
  85. DMA_EA          DB      "Invalid buffer ID.",0
  86. DMA_EB          DB      "Copy out of buffer range.",0
  87. DMA_EC          DB      "Invalid DMA channel number.",0
  88. DMA_ED          DB      "Buffer not word-aligned.",0
  89. _dma_errlist DD DMA_E0, DMA_E1, DMA_E2, DMA_E3, DMA_E4, DMA_E5, DMA_E6, DMA_E7,\
  90.                 DMA_E8, DMA_E9, DMA_EA, DMA_EB, DMA_EC, DMA_ED
  91. _dma_errno   DW 0
  92.  
  93. ;char near *dma_errlist[]
  94. ;int dma_errno
  95. PUBLIC _dma_errlist,_dma_errno
  96.  
  97.                 CODESEG
  98. MACRO zero reg
  99.       xor reg,reg
  100. ENDM zero
  101.  
  102. MACRO adjust reg          ; Adjust register port for 2nd DMA cont
  103. local no_adjust
  104.       cmp [dmac2], 0
  105.       jz no_adjust
  106.       shl reg,1
  107.       add reg,0C0h
  108. no_adjust:
  109. ENDM adjust
  110.  
  111. PUBLIC _dma_setup,_prevent_dma,_allow_dma,_dma_count,_dma_addr
  112. ;+---------------------------------------------------------------------------+
  113. ;| int dma_setup(int Channel,char far *Buffer,unsigned Length,int Dir)       |
  114. ;| ------------------------------------------------------------------------- |
  115. ;| Channel = 0-7                                                             |
  116. ;| Buffer  = Address of data to transfer                                     |
  117. ;| Length  = Length of data to transfer                                      |
  118. ;| Dir     = Direction to move bytes.  1 == Out to the BUS (TO the card)     |
  119. ;|                                     0 == In from the BUS and cards.       |
  120. ;| ------------------------------------------------------------------------- |
  121. ;| Returns: 0 if no errors (dma_errno == 0)                                  |
  122. ;|         -1 if errors occurred (dma_errno set to indicate error.)          |
  123. ;+---------------------------------------------------------------------------+
  124. PROC _dma_setup
  125. ARG Channel:WORD,Buffer:DWORD,Len:WORD,Dir:WORD
  126.   push bp
  127.   mov  bp,sp
  128.         push bx cx dx si di
  129.   pushf
  130.  
  131.         mov  [_dma_errno],0
  132. ;Convert seg:ofs Buffer to 20-bit physical address
  133. ;Assumes operating in 8086/real-Mode
  134.         mov  bx,[WORD PTR Buffer]
  135.         mov  ax,[WORD PTR Buffer+2]
  136.         mov  cl,4
  137.         rol  ax,cl
  138.         mov  ch,al
  139.         and  al,0F0h
  140.         add  ax,bx
  141.         adc  ch,0
  142.         and  ch,0Fh
  143.         mov  di,ax
  144. ; (ch << 16) + di == The physical buffer base.
  145.  
  146. ; Check channel number range
  147.         mov  [dmac2],0
  148.         mov  bx,[Channel]
  149.         cmp  bx,7
  150.         jbe  @@Set_base
  151.         mov  [_dma_errno],0Ch
  152.         mov  ax,-1
  153.         jmp  @@ExitPt
  154. @@Set_base:
  155.         cmp  bx,4
  156.         jb   @@OkChannel
  157.         sub  bx,4
  158.         inc  [dmac2]
  159. @@OkChannel: ; BX contains the adjusted channel number
  160.  
  161. ;Determine which command byte will be written later
  162.         cmp  [WORD PTR Dir],0
  163.         jnz  SHORT @@Do_Read
  164.         mov  al,write_cmd
  165.         jmp  short @@Do_Mode
  166. @@Do_Read:
  167.         mov  al,read_cmd
  168. @@Do_Mode:
  169.         add  al,bl
  170.         zero ah
  171.         mov  si,ax
  172.         mov  ax,set_cmd    ;allow dma requests
  173.         add  al,bl
  174.         mov  cl,al
  175. ;si contains READ/WRITE command for DMA controller
  176. ;cl contains confirmation command for DMA controller
  177.         shl  bx,1
  178. ;bx == Port # Channel*2
  179.  
  180. ;-------------------------------------------------------------------------
  181. ; Calculations have been done ahead of time to minimize time with
  182. ; interrupts disabled.
  183. ;
  184. ; ch:di == physical base address (must be on word boundary for 16 bits)
  185. ;
  186. ; cl == Confirmation command    (Unmasks the channel)
  187. ;
  188. ; bx == I/O port Channel*2      (This is where the address is written)
  189. ;
  190. ; si == Mode command for DMA
  191. ;-------------------------------------------------------------------------
  192.         mov  ax,di              ;Let's check the address to see if we
  193.         add  ax,[Len]           ;span a page boundary with our length
  194.         jnc  @@BoundaryOk       ;Do we?
  195.         mov  [_dma_errno],2     ; y: Error #2
  196.         mov  ax,-1              ;    Return -1
  197.         jmp  @@ExitPt           ;    See ya...
  198. @@BoundaryOk:                   ; n: Continue with action
  199. ; Now we shift the address and word count right one bit if in 16 bit mode.
  200.         cmp  [dmac2],0
  201.         jz   @@AddrOk
  202.         shr  ch,1
  203.         rcr  di,1
  204.         shl  ch,1
  205.         jc   @@BadAddr
  206.         rcr  [Len],1            ;Odd byte lengths are rounded down
  207.         jmp  @@AddrOk
  208. @@BadAddr:
  209.         mov  [_dma_errno],13    ;Word-alignment error message
  210.         mov  ax,-1
  211.         jmp  @@ExitPt
  212. @@AddrOk:
  213.         cli                     ;Disable interrupts while mucking with DMA
  214.  
  215. ;The "byte pointer" is also known as the LSB/MSB flip flop.
  216. ;By writing any value to it, the DMA controller registers are prepared
  217. ;to accept the address and length values LSB first.
  218.         mov  dx,byte_ptr        ;Reset byte pointer Flip/flop
  219.         adjust dx
  220.         out  dx,al              ;All we have to do is write to it
  221.  
  222.         mov  ax,di              ;ax=LSW of 20-bit address
  223.         mov  dx,bx              ;dx=DMAC Base Address port
  224.         adjust dx
  225.         out  dx,al              ;Store LSB
  226.         mov  al,ah
  227.         out  dx,al              ;Store next byte
  228.  
  229. ;Write length to port (Channel*2 + 1)
  230.         mov  ax,[Len]
  231.         mov  dx,bx
  232.         inc  dx                 ;dx=DMAC Count port
  233.         adjust dx
  234.         out  dx,al              ;Write LSB of Length
  235.         mov  al,ah
  236.         out  dx,al              ;Write MSB
  237.  
  238.         mov  bx,[Channel]
  239.         shl  bx,1
  240.         mov  dx,[bx + OFFSET page_table]  ;dx=page register port
  241.         mov  al,ch              ;al=Page number
  242.         out  dx,al              ;Store the page
  243.  
  244.         mov  dx,Mode            ;dx=DMAC mode register
  245.         adjust dx
  246.         mov  ax,si              ;Load pre-calculated mode
  247.         out  dx,al              ;Write it to the DSP
  248.  
  249. IFNDEF NO_START
  250.         mov  dx,DMA_Mask        ;dx=DMAX DMA_Mask register
  251.         adjust dx
  252.         mov  al,cl              ;al=pre-calulated DMA_Mask value
  253.         out  dx,al              ;Write DMA_Mask (allow dma on this channel)
  254. ENDIF
  255.         mov  ax,0               ;Return with no error
  256.  
  257. @@ExitPt:                       ;Restore stack and return
  258.         popf
  259.         pop  di si dx cx bx
  260.         pop  bp
  261.         ret
  262. ENDP _dma_setup
  263.  
  264. ;+---------------------------------------------------------------------------+
  265. ;| int prevent_dma(int Channel)                                              |
  266. ;| ------------------------------------------------------------------------- |
  267. ;| Channel = 0-7                                                             |
  268. ;|         Prevents DMA requests from Channel by masking bit in DMA_C.       |
  269. ;| ------------------------------------------------------------------------- |
  270. ;| Returns 0 if Ok, -1 and sets dma_errno on error                           |
  271. ;+---------------------------------------------------------------------------+
  272. PROC _prevent_dma
  273. ARG Channel:Word
  274.   push bp
  275.         mov  bp,sp
  276.         mov  ax, reset_cmd      ; Disable DMA requests
  277. Send_ax_mask:                   ; Jump here from _allow_dma
  278.         push dx
  279.         mov  [_dma_errno],0
  280. ; Check channel number range
  281.         mov  [dmac2],0
  282.         mov  bx,[Channel]
  283.         cmp  bx,7
  284.         jbe  @@Set_base
  285.         mov  [_dma_errno],0Ch
  286.         mov  ax,-1
  287.         jmp  @@Exit_Pt
  288. @@Set_base:
  289.         cmp  bx,4
  290.         jb   @@OkChannel
  291.         sub  bx,4
  292.         inc  [dmac2]
  293. @@OkChannel:
  294.         mov  dx,DMA_Mask
  295.         adjust dx
  296.         add  ax,bx        ; Add the adjusted channel number
  297.         out  dx,al
  298.         mov  ax,0
  299. @@Exit_Pt:
  300.         pop  dx
  301.         pop  bp
  302.         ret
  303. ENDP _prevent_dma
  304.  
  305. ;+---------------------------------------------------------------------------+
  306. ;| int allow_dma(int Channel)                                                |
  307. ;| ------------------------------------------------------------------------- |
  308. ;| Channel = 0-7                                                             |
  309. ;|         Unmasks DMA on the specified channel.                             |
  310. ;| ------------------------------------------------------------------------- |
  311. ;| Returns 0 if Ok, -1 and sets dma_errno on error                           |
  312. ;+---------------------------------------------------------------------------+
  313. PROC _allow_dma
  314. ARG Channel:Word
  315.   push bp
  316.         mov  bp,sp
  317.         mov  ax,set_cmd
  318.         jmp  Send_ax_mask
  319. ENDP _allow_dma
  320.  
  321. ;+---------------------------------------------------------------------------+
  322. ;| int dma_count(Channel)                                                    |
  323. ;| ------------------------------------------------------------------------- |
  324. ;| Channel = 0-7                                                             |
  325. ;| ------------------------------------------------------------------------- |
  326. ;| Returns: -1 if DMA transaction completed                                  |
  327. ;|         (It returns the number of bytes/words left to transfer)           |
  328. ;| dma_errno == 0 if no error, otherwise equals error number                 |
  329. ;+---------------------------------------------------------------------------+
  330. PROC _dma_count
  331. ARG Channel:Word
  332.   push bp
  333.         mov  bp,sp
  334.         pushf
  335.         push dx
  336. ; Check channel number range
  337.         mov  [dmac2],0
  338.         mov  bx,[Channel]
  339.         cmp  bx,7
  340.         jbe  @@Set_base
  341.         mov  [_dma_errno],0Ch
  342.         mov  ax,-1
  343.         jmp  @@Exit_Pt
  344. @@Set_base:
  345.         cmp  bx,4
  346.         jb   @@OkChannel
  347.         sub  bx,4
  348.         inc  [dmac2]
  349. @@OkChannel:
  350.         mov  dx,bx
  351.         shl  dx,1
  352.         add  dx,count
  353.         adjust dx
  354.         cli
  355.         in   al,dx
  356.         mov  ah,al
  357.         in   al,dx
  358.         xchg al,ah
  359. @@Exit_Pt:
  360.         pop  dx
  361.         popf     ; re-enables interrupts
  362.         pop  bp
  363.         ret
  364. ENDP _dma_count
  365.  
  366. ;+---------------------------------------------------------------------------+
  367. ;| unsigned dma_addr(Channel)                                                |
  368. ;| ------------------------------------------------------------------------- |
  369. ;| Channel = 0-7                                                             |
  370. ;| ------------------------------------------------------------------------- |
  371. ;| Returns: Current address word of that channel                             |
  372. ;| Value must be multiplied by 2 for a 16-bit channel.                       |
  373. ;| It is best to start at offset 0, ie on a 64K boundary                     |
  374. ;| dma_errno == 0 if no error, otherwise equals error number                 |
  375. ;+---------------------------------------------------------------------------+
  376. PROC _dma_addr
  377. ARG Channel:Word
  378.   push bp
  379.         mov  bp,sp
  380.         pushf
  381.         push dx
  382. ; Check channel number range
  383.         mov  [dmac2],0
  384.         mov  bx,[Channel]
  385.         cmp  bx,7
  386.         jbe  @@Set_base
  387.         mov  [_dma_errno],0Ch
  388.         mov  ax,-1
  389.         jmp  @@Exit_Pt
  390. @@Set_base:
  391.         cmp  bx,4
  392.         jb   @@OkChannel
  393.         sub  bx,4
  394.         inc  [dmac2]
  395. @@OkChannel:
  396.         mov  dx,bx
  397.         shl  dx,1
  398.         adjust dx
  399.         cli
  400.         in   al,dx
  401.         mov  ah,al
  402.         in   al,dx
  403.         xchg al,ah
  404. @@Exit_Pt:
  405.         pop  dx
  406.         popf
  407.         pop  bp
  408.         ret
  409. ENDP _dma_addr
  410. END
  411.